/*
 * CGameWorld.h
 *
 *  Created on: 03.01.2013
 *      Author: Kirill Blinov
 *      Copyright: LGPL
 */

#ifndef CGAMEWORLD_H_
#define CGAMEWORLD_H_

#include "../BasicTypes.h"
#include <vector>
#include <list>
#include <string>
#include <boost/unordered_map.hpp>

#include "IUnit.h"

class CGameWorld
{
public:
	struct CPhysicsParam
	{
		//TODO Add physics engine configuration from world file
	};

	struct CCameraParam
	{
		//TODO Add camera configuration from world file
	};

	struct CGameObjectParam
	{
		CGameObjectParam( const std::string UnitTypeName,
						  const std::string UnitName,
						  const glm::vec3 Position,
						  const glm::quat Orientation,
						  const float Speed ) : unitTypeName( UnitTypeName ),
								  	  	  	    unitName( UnitName ),
								  	  	  	    position( Position ),
								  	  	  	    orientation( Orientation ),
								  	  	  	    speed( Speed )
		{}

		std::string   unitTypeName;
		std::string   unitName;
		glm::vec3     position;
		glm::quat     orientation;
		float         speed;
	};

private:
	CPhysicsParam                 *worldPhysicsParam;
	CCameraParam                  *worldCameraParam;
	std::vector<CGameObjectParam*> worldGameObjectParams;

	std::vector<IUnit*>                                 		 gameUnitsArray;
	std::list<IUnit*>                                            gameUnitsList;
	boost::unordered_map< std::string, std::pair<IUnit*,UINT> >  gameUnitsSet;

	UINT unitIteratorsAliveCounter;

private:
	CGameWorld() : worldPhysicsParam(NULL), worldCameraParam(NULL),
				   unitIteratorsAliveCounter(0)
	{ assert(false); };

public:
	/*
	 * This class is ugly iterator used to iterate over units in game world
	 * without any knowledge about containers internal structure
	 */
	class CGameWorldUnitIterator
	{
	private:
		std::vector<IUnit*> *unitsArray;
		std::list<IUnit*>   *unitsList;

		std::vector<IUnit*>::iterator arrayIterator;
		std::list<IUnit*>::iterator   listIterator;

		CGameWorld* owner;

	protected:
		CGameWorldUnitIterator( std::vector<IUnit*> *UnitsArray,
				                std::list<IUnit*> *UnitsList,
				                CGameWorld* Owner ) :
				unitsArray( UnitsArray ), unitsList( UnitsList ), owner( Owner )
		{
			arrayIterator = unitsArray->begin();
			listIterator = unitsList->begin();
		}

	public:
		enum ITERATOR_POSITION
		{
			ITERATOR_POSITION_BEGIN,
			ITERATOR_POSITION_END
		};

	public:
		virtual ~CGameWorldUnitIterator()
		{
			owner->DestroyWorldUnitsIterator( this );
		}

		/*
		 * Returns next unit in game world. First iterate over units
		 * stored in an array and then over stored in a list.
		 *
		 * NULL is returned only after all units are iterated.
		 */
		IUnit* GetNextUnit()
		{
			IUnit* Result = NULL;

			if ( arrayIterator != unitsArray->end() )
			{
				Result = *arrayIterator;
				arrayIterator++;
			}
			else
			{
				if ( listIterator != unitsList->end() )
				{
					Result = *listIterator;
					listIterator++;
				}
			}
			return Result;
		}

		/*
		 * Returns previous unit in game world. First iterate over units
		 * stored in an array and then over stored in a list.
		 *
		 * NULL is returned only after all units are iterated.
		 */
		IUnit* GetPreviousUnit()
		{
			IUnit* Result = NULL;

			if ( arrayIterator != unitsArray->begin() )
			{
				Result = *arrayIterator;
				arrayIterator--;
			}
			else
			{
				if ( listIterator != unitsList->begin() )
				{
					Result = *listIterator;
					listIterator--;
				}
			}
			return Result;
		}

		CGameWorld* GetOwner()const { return owner; }

		void ResetIterator( ITERATOR_POSITION NewPosition )
		{
			switch( NewPosition )
			{
				break; case ITERATOR_POSITION_BEGIN:
				{
					arrayIterator = unitsArray->begin();
					listIterator  = unitsList->begin();
				}
				break; case ITERATOR_POSITION_END:
				{
					arrayIterator = unitsArray->end();
					listIterator  = unitsList->end();
				}
				break; default:
				{
					assert( false );
				}
				break;
			};
		}

		UINT GetUnitsCount()
		{
			UINT Result = unitsArray->size() + unitsList->size();
			return Result;
		}

	};

public:
	// This optimization flags say where to store a new unit in gameUnitsArray or
	// in gameUnitsList. gameUnitsSet stores the new unit too anyway.
	static const short CREATE_UNIT_OPTIMAZE_ITERATION = 0;
	static const short CREATE_UNIT_OPTIMAZE_DELETE = 1;

	static const UINT CREATE_UNIT_MASK = 0x80000000;

public:
	CGameWorld( CPhysicsParam *WorldPhysicsParam,
			    CCameraParam *WorldCameraParam,
			    std::vector<CGameObjectParam*> WorldGameObjectParams );

	/*
	 * Remember, a game world can't be destroyed until at least one
	 * CGameWorldUnitIterator instance belongs to this class exists.
	 */
	virtual ~CGameWorld();

	/*
	 * Create a unit from template and register it in game world
	 */
	bool CreateUnit( const CGameObjectParam &InitialParams,
					 const bool EnableNameIndexing = true,
					 const short OptimazeOption = CREATE_UNIT_OPTIMAZE_ITERATION,
					 IUnit** CreatedUnit = NULL );

	/*
	 * Returns a unit with specified name
	 */
	IUnit* GetUnit( const std::string Name ) const;

	/*
	 * Creates an iterator for game world's units can be used to go over all of them
	 */
	CGameWorldUnitIterator* GetWorldUnitsIterator() const;

	/*
	 * Deletes a game world's units iterator. All world's iterators must be deleted
	 * prior to destroy game world instance.
	 */
	void DestroyWorldUnitsIterator( CGameWorldUnitIterator* Iterator )const;

	void DeleteUnit( const std::string Name );

	void DeleteUnit( IUnit* GetUnit );

	/*
	 * Deletes all currently exist units and create new instances
	 * from worldGameObjectParams.
	 */
	void ResetGameWorld();
};

#endif /* CGAMEWORLD_H_ */
